home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / WASTE 1.2 Distribution / WASTE 1.2 / WELineLayout.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-18  |  15.1 KB  |  527 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELineLayout.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Line Layout, Getting and Setting Variables, etc.
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal void WEStopInlineSession(WEHandle hWE)
  18. {
  19.     WEPtr pWE = *hWE;
  20.  
  21. // call FixTSMDocument() only if the inlint input area is actually "open"
  22.     if ((pWE->tsmAreaStart != kInvalidOffset) && (pWE->tsmReference != nil))
  23.         FixTSMDocument(pWE->tsmReference);
  24. }
  25.  
  26. INLINE pascal void _WERemoveLine(SInt32 lineIndex, WEPtr pWE)
  27. {
  28.     // remove the line
  29.     _WERemoveBlock((Handle) pWE->hLines, sizeof(LineRec), lineIndex * sizeof(LineRec));
  30.  
  31.     // decrement line count
  32.     pWE->nLines--;
  33. }
  34.  
  35. pascal OSErr _WEInsertLine(SInt32 lineIndex, const LineRec *pLine, WEPtr pWE)
  36. {
  37.     // insert the specified element in the line array
  38.     OSErr err;
  39.  
  40.     // do the insertion
  41.     if ((err = _WEInsertBlock((Handle) pWE->hLines, pLine, sizeof(LineRec), lineIndex * sizeof(LineRec))) != noErr)
  42.         return err;
  43.  
  44.     // increment line count
  45.     pWE->nLines++;
  46.     return noErr;
  47. }
  48.  
  49. pascal void _WEBumpOrigin(SInt32 lineIndex, SInt32 deltaOrigin, WEPtr pWE)
  50. {
  51.     LineRec *pLine = *pWE->hLines + lineIndex;
  52.     SInt32 nLines = pWE->nLines;
  53.  
  54.     // loop through the line run array adjusting the lineOrigin fields
  55.     for ( ; lineIndex <= nLines; lineIndex++ )
  56.     {
  57.         pLine->lineOrigin += deltaOrigin;
  58.         pLine++;
  59.     }
  60. }
  61.  
  62. pascal SInt32 _WEFindLineBreak(SInt32 lineStart, WEHandle hWE)
  63. {
  64.     // Find where to break the line beginning at lineStart
  65.     // the current graphics port must be already set up correctly
  66.  
  67.     WEPtr pWE = *hWE;    // assume WE record is already locked
  68.     Ptr pText;
  69.     SInt32 offset, breakOffset;
  70.     SInt32 textLength;
  71.     SInt32 remainingLength;
  72.     SInt32 segmentStart, segmentEnd;
  73.     SInt32 runIndex;
  74.     WERunInfo runInfo;
  75.     Fixed pixelWidth;
  76.     ScriptCode script, previousScript;
  77.     Boolean isBreak = false;
  78.  
  79.     offset = lineStart;
  80.     pText = *pWE->hText + offset;
  81.     remainingLength = pWE->textLength - offset;
  82.  
  83.     // find the style run index corresponding to the first segment on this line
  84.     runIndex = _WEOffsetToRun(offset, hWE);
  85.  
  86.     // initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity
  87.     pixelWidth = BSL((pWE->destRect.right - pWE->destRect.left), 16);
  88.  
  89.     // STYLE SEGMENT LOOP
  90.     do
  91.     {
  92.  
  93.         // get style run information for the current style run
  94.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  95.         runIndex++;
  96.  
  97.         // set text attributes in the graphics port
  98.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  99.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  100.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  101.  
  102.         // if we're handling multiscript text, keep track of script boundaries
  103.         if (BTST(pWE->flags, weFNonRoman))
  104.         {
  105.             // what is the script for this segment?
  106.             script = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  107.  
  108.             // have we crossed a script run boundary in the middle of a line?
  109.             if ((runInfo.runStart > offset) && (script != previousScript))
  110.             {
  111.                 // leave behind the all previous segments on this line
  112.                 offset = runInfo.runStart;
  113.                 pText = *pWE->hText + offset;
  114.                 remainingLength = pWE->textLength - offset;
  115.             }
  116.             previousScript = script;
  117.         } // if non-Roman
  118.  
  119.         // we'll pass textLength as the second parameter to the line break hook
  120.         // although this parameter is declared as a long, StyledLineBreak uses only
  121.         // the low word, so make sure it doesn't trespass the 32,767 byte threshold!
  122.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  123.  
  124.         // calculate segmentStart and segmentEnd relative to offset
  125.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  126.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  127.  
  128.         // set breakOffset to a non-zero value for the first script run on the line,
  129.         // set it to zero for all subsequent script runs
  130.         breakOffset = (offset == lineStart);
  131.  
  132. #if WASTE_OBJECTS
  133.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  134.         {
  135.             // EMBEDDED OBJECT
  136.             // subtract object width from pixelWidth
  137.             pixelWidth -= BSL((*runInfo.runAttrs.runStyle.tsObject)->objectSize.h, 16);
  138.  
  139.             // stop looping if pixelWidth has gone negative
  140.             isBreak = (pixelWidth < 0);
  141.             breakOffset = isBreak ? segmentStart : segmentEnd;
  142.         }
  143.         else
  144. #endif
  145.         {
  146.             // REGULAR TEXT
  147.             isBreak = (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd,
  148.                 &pixelWidth, &breakOffset, hWE, pWE->lineBreakHook) != smBreakOverflow);
  149.         }
  150.  
  151.         // break the line anyway when we reach the end of the text
  152.         if (segmentEnd >= remainingLength)
  153.             isBreak = true;
  154.  
  155.     } while (!isBreak);
  156.  
  157.     // return the offset from lineStart to the break point
  158.     return (offset - lineStart) + breakOffset;
  159. }
  160.  
  161. pascal void _WECalcHeights(SInt32 rangeStart, SInt32 rangeEnd, SInt16 *lineAscent, SInt16 *lineDescent,
  162.         WEHandle hWE)
  163. {
  164.     // Find the maximum ascent and descent values between rangeStart and rangeEnd
  165.     // the WE record must be already locked
  166.     // the current graphics port must be already set up correctly
  167.  
  168.     SInt32 runIndex;
  169.     WERunInfo runInfo;
  170.     SInt16 runAscent, runDescent;
  171.  
  172.     *lineAscent = 1;
  173.     *lineDescent = 0;
  174.  
  175.     // find the style run index corresponding to the first segment on this line
  176.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  177.  
  178.     // STYLE SEGMENT LOOP
  179.     do
  180.     {
  181.         // get style run information for the current style run
  182.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  183.         runIndex++;
  184.  
  185.         // calculate ascent and descent (actually, descent + leading) values for this style run
  186.  
  187. #if WASTE_OBJECTS
  188.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  189.         {
  190.             // EMBEDDED OBJECT
  191.             runAscent = (*runInfo.runAttrs.runStyle.tsObject)->objectSize.v;
  192.             runDescent = 0;
  193.         }
  194.         else
  195. #endif
  196.         {
  197.             // REGULAR TEXT
  198.             runAscent = runInfo.runAttrs.runAscent;
  199.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  200.         }
  201.  
  202.         // save the maximum values in lineAscent and lineDescent
  203.         if (runAscent > *lineAscent)
  204.             *lineAscent = runAscent;
  205.  
  206.         if (runDescent > *lineDescent)
  207.             *lineDescent = runDescent;
  208.  
  209.         // keep looping until we reach rangeEnd
  210.     } while (runInfo.runEnd < rangeEnd);
  211. }
  212.  
  213. pascal OSErr _WERecalBreaks(SInt32 *startLine, SInt32 *endLine, WEHandle hWE)
  214. {
  215.     // Recalculates line breaks, line heights and ascents for all the text or for a portion of it.
  216.     // On entry, startLine and endLine define a range of lines to recalculate.
  217.     // On exit, startLine to endLine defines the range of lines actually recalculated
  218.     // the WE record must already be locked
  219.  
  220.     WEPtr pWE = *hWE;
  221.     LinePtr pLine;
  222.     LineRec lineInfo, oldLineInfo;
  223.     SInt32 lineIndex;
  224.     SInt32 recalThreshold;
  225.     SInt32 lineOffset;
  226.     SInt16 lineAscent, lineDescent;
  227.     SInt32 textHeight;
  228.     Boolean saveTextLock;
  229.     QDEnvironment saveEnvironment;
  230.     OSErr err = noErr;
  231.  
  232.     // lock the text
  233.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  234.  
  235.     // find the character offset that must be necessarily reached before we can
  236.     // even consider the possibility of stopping the recalculation process
  237.     // this offset, recalThreshold, is the last character on endLine _before_ recalculation
  238.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  239.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  240.  
  241.     // we start recalculating line breaks from the line actually _preceding_ startLine,
  242.     // since editing startLine may cause part of its text to fit on the preceding line
  243.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  244.  
  245.     // find where in the text recalculation should begin
  246.     lineInfo = (*pWE->hLines)[lineIndex];
  247.  
  248.     // save the Quickdraw environment
  249.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  250.  
  251.     // MAIN LINE BREAKING LOOP
  252.     do
  253.     {
  254.         // find where to break the current line
  255.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  256.  
  257.         // make sure we advance at least by one character (unless we reached the end of text)
  258.         if ((lineOffset <= 0) && (lineInfo.lineStart < pWE->textLength))
  259.             lineOffset = 1;
  260.  
  261.         // calculate ascent and descent values for this line
  262.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  263.  
  264.         // save the maximum line ascent for this line in the line array
  265.         pLine = *pWE->hLines + lineIndex;
  266.         pLine->lineAscent = lineAscent;
  267.  
  268.         // increment counters (go to the next line array entry)
  269.         lineIndex++;
  270.         lineInfo.lineStart += lineOffset;
  271.         lineInfo.lineOrigin += (lineAscent + lineDescent);
  272.         pLine++;
  273.  
  274.         // compare the newly calculated line start with the old value
  275.         // if the new line start comes before the old line start, insert a new element
  276.         oldLineInfo = *pLine;
  277.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart))
  278.         {
  279.             if ((err = _WEInsertLine(lineIndex, &lineInfo, pWE)) != noErr)
  280.                 goto cleanup;
  281.         }
  282.         else
  283.         {
  284.             // overwrite the old element
  285.             pLine->lineStart = lineInfo.lineStart;
  286.             pLine->lineOrigin = lineInfo.lineOrigin;
  287.  
  288.             // remove all further elements which have a lineStart field
  289.             // less than or equal to the current one
  290.             while((lineIndex < pWE->nLines) && (lineInfo.lineStart >= (pLine + 1)->lineStart))
  291.                 _WERemoveLine(lineIndex + 1, pWE);
  292.  
  293.             // if the new line start is the same as the old one...
  294.             if (lineInfo.lineStart == oldLineInfo.lineStart)
  295.             {
  296.                 // ...and recalThreshold has been reached, we can stop recalculating line breaks
  297.                 if (lineInfo.lineStart >= recalThreshold)
  298.                 {
  299.                     // although line breaks need not be changed from lineIndex on,
  300.                     // the lineOrigin fields may need to be changed
  301.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin)
  302.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  303.  
  304.                     // exit from the line breaking loop
  305.                     goto cleanup;
  306.                 }
  307.             }
  308.             else
  309.             {
  310.                 // otherwise, the new line start comes after the old line start...
  311.                 // if the current line is the one preceding startLine, warn our caller about this
  312.                 if ((lineIndex > 0) && (lineIndex == *startLine))
  313.                     *startLine = lineIndex - 1;
  314.             }
  315.         }
  316.     } while(lineInfo.lineStart < pWE->textLength);
  317.  
  318. cleanup:
  319.     // calculate total text height
  320.     textHeight = WEGetHeight(0, pWE->nLines, hWE);
  321.  
  322.     // quirk: if the last character in the text is a carriage return, the caret appears
  323.     // below the last line, so in this case we need to add the extra height to textHeight
  324.     if (WEGetChar(pWE->textLength - 1, hWE) == kEOL)
  325.         textHeight += WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  326.  
  327.     // if total text height has changed, remember to call the scroll callback later
  328.     if (textHeight != pWE->destRect.bottom - pWE->destRect.top)
  329.         BSET(pWE->flags, weFDestRectChanged);
  330.  
  331.     // set destRect.bottom to destRect.top + total text height
  332.     pWE->destRect.bottom = pWE->destRect.top + textHeight;
  333.  
  334.     // return through endLine the index of the last line affected by recalculation
  335.     *endLine = lineIndex - 1;
  336.  
  337.     // make sure startLine isn't greater than endLine
  338.     if (*startLine > *endLine)
  339.         *startLine = *endLine;
  340.  
  341.     // unlock the text
  342.     _WESetHandleLock(pWE->hText, saveTextLock);
  343.  
  344.     // restore the Quickdraw environment
  345.     _WERestoreQDEnvironment(&saveEnvironment);
  346.  
  347.     // return result code
  348.     return err;
  349. }
  350.  
  351. static Boolean SLCalcSlop(LinePtr pLine, const WERunAttributes *pAttrs,
  352.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  353.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  354. {
  355.     struct SLCalcSlopData *cd = (struct SLCalcSlopData *) callbackData;
  356.     SInt16 segmentWidth;
  357.     Fixed segmentProportion;
  358.     Boolean isEndOfLine;
  359.  
  360.     // see if this text segment ends with a carriage return, or if we've reached the
  361.     // end of the text (in which case we don't want any justification to take place)
  362.     isEndOfLine = (segmentStart + segmentLength >= (*hWE)->textLength) ||
  363.                   (pSegment [ segmentLength - 1 ] == kEOL);
  364.  
  365.     // if this is the first segment on the line, reset line totals
  366.     if (IS_FIRST_RUN(styleRunPosition))
  367.     {
  368.         cd->totalSlop = cd->lineWidth;
  369.         cd->totalProportion = 0;
  370.     }
  371.  
  372. #if WASTE_OBJECTS
  373.     if (pAttrs->runStyle.tsObject != nil)
  374.     {
  375.         // EMBEDDED OBJECT
  376.         // segment width is just object width; no extra space can be applied for justification
  377.         segmentWidth = (*pAttrs->runStyle.tsObject)->objectSize.h;
  378.         segmentProportion = 0;
  379.     }
  380.     else
  381. #endif
  382.     {
  383.         // REGULAR TEXT
  384.  
  385.         // if this is the last segment on the line, strip trailing spaces
  386.         if (IS_LAST_RUN(styleRunPosition))
  387.         {
  388.             segmentLength = VisibleLength(pSegment, segmentLength);
  389.         }
  390.  
  391.         // measure this segment
  392.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  393.  
  394.         // calculate the proportion of extra space to apply to this text segment
  395.         segmentProportion = PortionLine(pSegment, segmentLength,
  396.                 styleRunPosition, kOneToOneScaling, kOneToOneScaling);
  397.     }
  398.  
  399.     // keep track of line totals
  400.     cd->totalSlop -= segmentWidth;
  401.     cd->totalProportion += segmentProportion;
  402.  
  403.     // if this is the last segment on the line, save values in the line array
  404.     if (IS_LAST_RUN(styleRunPosition))
  405.     {
  406.         // make sure slop is non-negative
  407.         if (cd->totalSlop < 0)
  408.         {
  409.             cd->totalSlop = 0;
  410.         }
  411.         pLine->lineSlop = cd->totalSlop;
  412.         pLine->lineJustAmount = isEndOfLine ? 0 : FixDiv(BSL(cd->totalSlop, 16), cd->totalProportion);
  413.     }
  414.     return false;    // keep looping
  415. }
  416.  
  417. pascal void _WERecalSlops(SInt32 firstLine, SInt32 lastLine, WEHandle hWE)
  418. {
  419.     // Calculates the lineSlop and lineJustAmount fields
  420.     // of the line array for the specified lines
  421.  
  422.     WEPtr pWE = *hWE;
  423.     struct SLCalcSlopData cd;
  424.  
  425.     // we only need to bother if the user isn't using left justification
  426.     if (pWE->alignment == weFlushLeft)
  427.         return;
  428.  
  429.     // calculate slop and normalized slop proportion for all lines
  430.     cd.lineWidth = pWE->destRect.right - pWE->destRect.left;
  431.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, &cd, hWE);
  432. }
  433.  
  434. pascal WEAlignment WEGetAlignment(WEHandle hWE)
  435. {
  436.     return (*hWE)->alignment;
  437. }
  438.  
  439. pascal void WEGetSelection(SInt32 *selStart, SInt32 *selEnd, WEHandle hWE)
  440. {
  441.     WEPtr pWE = *hWE;
  442.  
  443.     *selStart = pWE->selStart;
  444.     *selEnd = pWE->selEnd;
  445. }
  446.  
  447. pascal void WESetDestRect(const LongRect *destRect, WEHandle hWE)
  448. {
  449.     (*hWE)->destRect = *destRect;
  450. }
  451.  
  452. pascal void WEGetDestRect(LongRect *destRect, WEHandle hWE)
  453. {
  454.     *destRect = (*hWE)->destRect;
  455. }
  456.  
  457. pascal void WESetViewRect(const LongRect *viewRect, WEHandle hWE)
  458. {
  459.     WEPtr pWE = *hWE;
  460.     Rect r;
  461.  
  462.     pWE->viewRect = *viewRect;
  463.  
  464.     // keep the viewRgn in sync with the view rectangle
  465.     WELongRectToRect(viewRect, &r);
  466.     RectRgn(pWE->viewRgn, &r);
  467. }
  468.  
  469. pascal void WEGetViewRect(LongRect *viewRect, WEHandle hWE)
  470. {
  471.     *viewRect = (*hWE)->viewRect;
  472. }
  473.  
  474. pascal SInt32 WEGetTextLength(WEHandle hWE)
  475. {
  476.     return (*hWE)->textLength;
  477. }
  478.  
  479. pascal SInt32 WECountLines(WEHandle hWE)
  480. {
  481.     return (*hWE)->nLines;
  482. }
  483.  
  484. pascal SInt32 WEGetHeight(SInt32 startLine, SInt32 endLine, WEHandle hWE)
  485. {
  486.     WEPtr pWE = *hWE;
  487.     LineArrayPtr pLines = *pWE->hLines;
  488.     SInt32 nLines = pWE->nLines;
  489.  
  490.     startLine = _WEPinInRange(startLine, 0, nLines);
  491.     endLine = _WEPinInRange(endLine, 0, nLines);
  492.     _WEReorder(&startLine, &endLine);
  493.     return pLines[endLine].lineOrigin - pLines[startLine].lineOrigin;
  494. }
  495.  
  496. pascal void WEGetLineRange(SInt32 lineNo, SInt32 *lineStart, SInt32 *lineEnd, WEHandle hWE)
  497. {
  498.     WEPtr pWE = *hWE;
  499.     LineRec *pLine;
  500.  
  501.     lineNo = _WEPinInRange(lineNo, 0, pWE->nLines - 1);
  502.     pLine = *pWE->hLines + lineNo;
  503.     if (lineStart != nil)
  504.         *lineStart = pLine[0].lineStart;
  505.     if (lineEnd != nil)
  506.         *lineEnd = pLine[1].lineStart;
  507. }
  508.  
  509. pascal Handle WEGetText(WEHandle hWE)
  510. {
  511.     return (*hWE)->hText;
  512. }
  513.  
  514. pascal SInt16 WEGetChar(SInt32 offset, WEHandle hWE)
  515. {
  516.     WEPtr pWE = *hWE;
  517.  
  518.     // sanity check: make sure offset is withing allowed bounds
  519.     if ((offset < 0) || (offset >= pWE->textLength))
  520.     {
  521.         return 0;
  522.     }
  523.  
  524.     // get the specified character (actually, byte)
  525.     return * (UInt8 *) (*pWE->hText + offset);
  526. }
  527.